#include <string.h>
#include "stdio.h"
#include "stdlib.h"
#include "crypto.h"
#include "des.h"
#include "base64.h"


void printHex(unsigned char *data , int len){
	int i = 0;
	for(i = 0; i < len; i++){
		//printf("%02X ",data[i]);
		printf("%02x",data[i]);
	}
	printf("\n");
}


static unsigned char* pack_padding_pkcs5(const unsigned char in[], int *len){
	unsigned char paddNum = 8 - *len % 8;
    unsigned char *data = (unsigned char *)malloc(*len + paddNum);
	int i = 0;
    memset(data, 0, *len + paddNum);
    memcpy(data, in, *len);
    for (i = 0; i < paddNum; i++) {
        data[*len + i] = paddNum;
    }
	*len = *len + paddNum;
	return data;
}

static unsigned char* unpack_padding_pkcs5(const unsigned char in[], int *len){
	unsigned char paddNum = in[*len - 1];
	if(paddNum > 8){
		*len = 0;
		return NULL;
	}
	*len = *len - paddNum;
	
	unsigned char *data = (unsigned char *)malloc(*len);
    memset(data, 0, *len);
    memcpy(data, in, *len );
	return data;
}


CRYPTO_RESULT des_encrypt(CRYPTO_TYPE type, const unsigned char key[], unsigned char keyLen, const unsigned char vi[8], const unsigned char in[],unsigned char **out,int *len){
	unsigned char *data = pack_padding_pkcs5(in,len);
	*out = (unsigned char *)malloc(*len); 
	if(type == DES_ECB || type == DES_CBC){
		mbedtls_des_context context;
		mbedtls_des_init(&context);
		mbedtls_des_setkey_enc(&context, key);
		if(type == DES_ECB){
			int i = 0;
			int num = *len / 8;
			for(i = 0; i < num; i++){
				mbedtls_des_crypt_ecb(&context, data + i * 8, *out + i * 8);
			}
		}else{
			unsigned char v[8] = {0};
			memcpy(v,vi,8);
			mbedtls_des_crypt_cbc(&context,MBEDTLS_DES_ENCRYPT,*len, v, data, *out);
		}

	}else if(type == DES3_ECB || type == DES3_CBC){
		mbedtls_des3_context context;
		if(keyLen != 8 && keyLen != 16 && keyLen != 24){
			return RESULT_ERROR;
		}
		unsigned char k[24] = {0};
		memcpy(k,key,keyLen);
		
		mbedtls_des3_init(&context);
		mbedtls_des3_set3key_enc(&context, k);
		if(type == DES3_ECB){
			int i = 0;
			int num = *len / 8;
			for(i = 0; i < num; i++){
				mbedtls_des3_crypt_ecb(&context, data + i * 8, *out + i * 8);
			}
		}else{
			unsigned char v[8] = {0};
			memcpy(v,vi,8);
			mbedtls_des3_crypt_cbc(&context,MBEDTLS_DES_ENCRYPT,*len, v, data, *out);
		}
	}
	free(data);
	return RESULT_OK;
}


CRYPTO_RESULT des_decrypt(CRYPTO_TYPE type, const unsigned char key[], unsigned char keyLen, const unsigned char vi[8], const unsigned char in[],unsigned char **out,int *len){
	if(*len % 8){
		return RESULT_ERROR;
	}
	unsigned char *data = (unsigned char *)malloc(*len); 
	if(type == DES_ECB || type == DES_CBC){
		mbedtls_des_context context;
		mbedtls_des_init(&context);
		mbedtls_des_setkey_dec(&context, key);
		if(type == DES_ECB){
			int i = 0;
			int num = *len / 8;
			for(i = 0; i < num; i++){
				mbedtls_des_crypt_ecb(&context, in + i * 8, data + i * 8);
			}
		}else{
			unsigned char v[8] = {0};
			memcpy(v,vi,8);
			mbedtls_des_crypt_cbc(&context,MBEDTLS_DES_DECRYPT,*len, v, in, data);
		}

	}else if(type == DES3_ECB || type == DES3_CBC){
		mbedtls_des3_context context;
		if(keyLen != 8 && keyLen != 16 && keyLen != 24){
			return RESULT_ERROR;
		}
		unsigned char k[24] = {0};
		memcpy(k,key,keyLen);
		
		mbedtls_des3_init(&context);
		mbedtls_des3_set3key_dec(&context, k);
		if(type == DES3_ECB){
			int i = 0;
			int num = *len / 8;
			for(i = 0; i < num; i++){
				mbedtls_des3_crypt_ecb(&context, in + i * 8, data + i * 8);
			}
		}else{
			unsigned char v[8] = {0};
			memcpy(v,vi,8);
			mbedtls_des3_crypt_cbc(&context,MBEDTLS_DES_DECRYPT,*len, v, in, data);
		}
	}
	*out = unpack_padding_pkcs5(data,len);
	free(data);
	if(*len == 0){
		return RESULT_ERROR;
	}
	return RESULT_OK;
}

CRYPTO_RESULT base64_en(const unsigned char in[],unsigned char **out,int *len){
	unsigned char *encode_out;
	encode_out = (unsigned char*)malloc(BASE64_ENCODE_OUT_SIZE(*len));
	*len = base64_encode(in, *len, (char *)encode_out);
	if(*len > 0){
		*out = (unsigned char*)malloc(*len + 1);
		memset(*out,0,*len + 1);
		memcpy(*out,encode_out,*len);
		free(encode_out);
		return RESULT_OK;
	}else{
		*out = (unsigned char*)malloc(1);
		*out[0] = 0;
		*len = 0;
		free(encode_out);
		return RESULT_ERROR;
	}
}

CRYPTO_RESULT base64_de(const unsigned char in[],unsigned char **out,int *len){
	unsigned char *decode_out;
	decode_out = (unsigned char*)malloc(BASE64_DECODE_OUT_SIZE(*len));
	*len = base64_decode((const char*)in, *len, decode_out);
	if(*len > 0){
		*out = (unsigned char*)malloc(*len);
		memset(*out,0,*len);
		memcpy(*out,decode_out,*len);
		free(decode_out);
		return RESULT_OK;
	}else{
		*out = (unsigned char*)malloc(1);
		*out[0] = 0;
		*len = 0;
		free(decode_out);
		return RESULT_ERROR;
	}
}